﻿using ogc = GeoAPI.Geometries;
using System.Windows.Media;
using DeepEarth.Map.Core;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Controls;
using GisSharpBlog.NetTopologySuite.IO;
using GisSharpBlog.NetTopologySuite.Geometries;
using System;
using DeepEarth.Core;

namespace DeepEarth.Toolkit.Geometry.OGC
{
    public class GeometryUtils
    {
        public static Location GetLocation(ogc.ICoordinate coordinate)
        {
            return new Location
            {
                Longitude = coordinate.X,
                Latitude = coordinate.Y,
                Altitude = coordinate.Z          
            };

        }

        public static List<Location> GetLocations(ogc.IGeometry geometry)
        {
            ogc.ICoordinate[] coordinates = geometry.Coordinates;
            return GetLocations(coordinates);
        }

        public static List<Location> GetLocations(ogc.ICoordinate[] coordinates)
        {
            List<Location> locations = coordinates.Select(c => new Location { Latitude = c.Y, Longitude = c.X, Altitude = c.Z }).ToList();
            return locations;
        }

        public static System.Windows.Media.Geometry GetFrameworkGeometry(Func<Location, System.Windows.Point> locationConverterFunction, ogc.IGeometry ogcGeometry)
        {
            switch (ogcGeometry.GeometryType.ToUpper())
            {
                case GeometryType.Point :
                    break;

                case GeometryType.LineString:
                    {
                        PathGeometry pg = new PathGeometry();
                        pg.Figures.Add(GetPathFigure(locationConverterFunction, ogcGeometry));
                        return pg;
                    }
                    break;

                case GeometryType.Polygon:
                    {
                        PathGeometry pg = new PathGeometry();
                        ogc.IPolygon polygon = ogcGeometry as ogc.IPolygon;
                        pg.Figures.Add(GetPathFigure(locationConverterFunction, polygon.Shell));
                        foreach (ogc.ILinearRing inner in polygon.InteriorRings)
                        {
                            pg.Figures.Add(GetPathFigure(locationConverterFunction, inner));
                        }
                        return pg;
                    }
                    break;

                case GeometryType.MultiLineString :
                    {
                        ogc.IGeometryCollection multiGeometry = ogcGeometry as ogc.IGeometryCollection;

                        PathGeometry pg = new PathGeometry();
                        foreach (ogc.IGeometry nonCollectionGeometry in multiGeometry.Geometries)
                        {
                            pg.Figures.Add(GetPathFigure(locationConverterFunction, nonCollectionGeometry));
                        }
                        return pg;
                    }
                    break;

                case GeometryType.MultiPolygon:
                case GeometryType.GeometryCollection:
                    {
                        ogc.IGeometryCollection multiGeometry = ogcGeometry as ogc.IGeometryCollection;

                        GeometryGroup gg = new GeometryGroup();
                        foreach (ogc.IGeometry geometry in multiGeometry.Geometries)
                        {
                            gg.Children.Add(GetFrameworkGeometry(locationConverterFunction, geometry));
                        }

                        return gg;
                    }
                    break;

            }

            return null;
        }

        public static PathFigure GetPathFigure(Func<Location, System.Windows.Point> locationConverterFunction, ogc.IGeometry geometry)
        {
            System.Diagnostics.Debug.Assert(!(geometry is ogc.IGeometryCollection));

            PathFigure figure = new PathFigure();

            List<Location> locations = GetLocations(geometry);

            int index = 0;
            foreach (Location location in locations)
            {
                //Add pixels relative to Layer size.
                System.Windows.Point pixel = locationConverterFunction(location);

                if (index == 0)
                {
                    figure.StartPoint = pixel;
                }
                else
                {
                    var ls = new System.Windows.Media.LineSegment { Point = pixel };
                    figure.Segments.Add(ls);
                }
                index++;
            }

            ogc.ILineString lineString = geometry as ogc.ILineString;
            if (lineString != null)
            {
                figure.IsClosed = lineString.IsClosed;
            }
            
            return figure;
        }

        public static Data.Geometry GetGeometryModel(ogc.IGeometry ogcGeometry)
        {
            switch (ogcGeometry.GeometryType.ToUpper())
            {
                case GeometryType.Point:
                    Data.PointGeometry pointGeometry = new Data.PointGeometry();
                    pointGeometry.Position = GetLocation(ogcGeometry.Coordinate);
                    return pointGeometry;

                case GeometryType.LineString:
                    {
                        Data.LineStringGeometry lsg = new Data.LineStringGeometry();
                        lsg.Locations = new System.Collections.ObjectModel.ObservableCollection<Location>(GetLocations(ogcGeometry.Coordinates));
                        return lsg;
                    }

                case GeometryType.Polygon:
                    {
                        ogc.IPolygon polygon = ogcGeometry as ogc.IPolygon;
                        Data.PolygonGeometry pg = new Data.PolygonGeometry();
                        pg.OuterRing = new System.Collections.ObjectModel.ObservableCollection<Location>(GetLocations(polygon.Shell)); ;


                        foreach (ogc.ILinearRing inner in polygon.InteriorRings)
                        {
                            pg.InnerRings.Add(new System.Collections.ObjectModel.ObservableCollection<Location>(GetLocations(inner)));
                        }
                        return pg;
                    }


                case GeometryType.MultiLineString:
                case GeometryType.MultiPolygon:
                case GeometryType.GeometryCollection:
                    {
                        ogc.IGeometryCollection multiGeometry = ogcGeometry as ogc.IGeometryCollection;

                        Data.GeometryCollection gc = new Data.GeometryCollection();
                        foreach (ogc.IGeometry geometry in multiGeometry.Geometries)
                        {
                            gc.Geometries.Add(GetGeometryModel(geometry));
                        }

                        return gc;
                    }
            }

            throw new ArgumentException("OGC Geometry is not of a known type");
        }

        public static ogc.IGeometry ReadWkb(byte[] wkb)
        {
            var wkbReader = new WKBReader(new GeometryFactory(new PrecisionModel(), 4326));
            return wkbReader.Read(wkb);
        }

    }
}
